﻿#region Usings

using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;

using Gonte.Reflection; 

#endregion

namespace Gonte.Xml.Serialization
{
    public class XmlCollectionSerializer
        : XmlObjectSerializer
    {
        #region Constructors

        protected internal XmlCollectionSerializer(Object @object)
            : base(@object)
        {
        } 

        #endregion

        #region Overridables

        public override void Serialize(XmlWriter writer, XmlSerializationContext context)
        {
            //ExceptionThrower.ThrowWhenNull("context", context);

            string elementName = GetElementName(context);
            writer.WriteStartElement(elementName);

            TypeXmlMapping typeXmlMapping = TypeXmlMapping.GetMapping(object_.GetType());

            ICollection collection = object_ as ICollection;

            PropertyInfo propertyInfo = null;

            if (context != null
                && !string.IsNullOrEmpty(context.PropertyName))
            {
                Object parent = context.Parent;
                propertyInfo = parent.GetType().GetProperty(context.PropertyName);

                if (Attribute.IsDefined(propertyInfo, typeof(XmlIgnoreAttribute)))
                {
                    return; // Do not serialize
                }
            }

            Type internalType = null;

            if (null != propertyInfo)
            {
                internalType = TypeHelper.UnwrapType(propertyInfo.PropertyType);
            }

            foreach (Object item in collection)
            {
                

                XmlObjectSerializer serializer = XmlObjectSerializer.GetSerializer(item);

                XmlSerializationContext childContext = new XmlSerializationContext();
                childContext.Parent = object_;

                if (context != null)
                {
                    childContext.PropertyName = context.PropertyName;
                    childContext.Attributes = context.Attributes;

                    if (null != internalType)
                    {
                        childContext.SerializeType = internalType.IsAbstract;
                    }
                    
                    childContext.Level = ++context.Level;
                }
                else
                {
                    childContext.Level = 1;
                }

                serializer.Serialize(writer, childContext);
            }

            writer.WriteEndElement();
        }

        protected override string GetElementName(XmlSerializationContext context)
        {
            Type type = object_.GetType();
            string elementName = GetXmlFriendlyName(context, type.Name);

            if (context != null
                && !string.IsNullOrEmpty(context.PropertyName))
            {
                elementName = context.PropertyName;

                Debug.WriteLine(string.Format("Name of element is: {0} for type: {1} and property name: {2}",
                elementName,
                type.Name,
                context.PropertyName));
            }
            else // It is the root
            {
                XmlRootAttribute xmlRootAttribute = CustomAttribute.GetCustomAttribute<XmlRootAttribute>(type, false);
                if (xmlRootAttribute != null
                    && !string.IsNullOrEmpty(xmlRootAttribute.ElementName))
                {
                    elementName = xmlRootAttribute.ElementName;
                }

                Debug.WriteLine(string.Format("Name of element is: {0} for root type: {1}",
                elementName,
                type.Name));
            }

            return elementName;
        } 

        #endregion

        #region Helpers

        private string GetXmlFriendlyName(XmlSerializationContext context, string collectionTypeName)
        {
            const char illegalXmlChar = '`';
            //int level = (context == null) ? 0 : context.Level;
            //string levelString = (level == 0) ? string.Empty : level.ToString();

            int position = collectionTypeName.IndexOf(illegalXmlChar);
            if (position > -1)
            {
                return collectionTypeName.Substring(0, position); // +levelString;
            }

            return collectionTypeName; // +levelString;
        }

        #endregion
    }
}
